D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\data.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2020-2021 Herald Project Contributors |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | // |
4 | | |
5 | | #ifndef HERALD_DATA_H |
6 | | #define HERALD_DATA_H |
7 | | |
8 | | #include <string> |
9 | | #include <iostream> |
10 | | |
11 | | #include "memory_arena.h" |
12 | | |
13 | | namespace herald { |
14 | | namespace datatype { |
15 | | |
16 | | /// \brief The main data workhorse class of the Herald API |
17 | | /// |
18 | | /// A vitally important part of Herald's datatypes. Many other types |
19 | | /// are actually just aliases or thin wrappers to the DataRef class. |
20 | | /// |
21 | | /// This class represents an arbitrarily long Big Endian list of std::byte. |
22 | | /// DataRef instances are used to encode Bluetooth advert data, to pass payloads |
23 | | /// between herald enabled devices, or to share data to and from backend systems. |
24 | | #ifndef HERALD_MEMORYARENA_MAX |
25 | | template <typename MemoryArenaT = MemoryArena<8192, 8>> |
26 | | #else |
27 | | #ifndef HERALD_MEMORYARENA_PAGE |
28 | | template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, 8>> |
29 | | #else |
30 | | template <typename MemoryArenaT = MemoryArena<HERALD_MEMORYARENA_MAX, HERALD_MEMORYARENA_PAGE>> |
31 | | #endif |
32 | | #endif |
33 | | class DataRef { |
34 | | public: |
35 | | /// \brief Creates a DataRef with no memory used |
36 | | DataRef() : entry() |
37 | 337 | { |
38 | 337 | ; |
39 | 337 | } |
40 | | /// \brief Takes control of another DataRef's memory allocation |
41 | | DataRef(DataRef&& other) |
42 | | : entry() |
43 | 27 | { |
44 | 27 | std::swap(entry,other.entry); |
45 | 27 | } |
46 | | /// \brief Initialises a DataRef from a std::uint8_t array of length `length` |
47 | | DataRef(const std::uint8_t* value, std::size_t length) : |
48 | 51 | entry(arena.allocate(length)) { |
49 | 525 | for (std::size_t i = 0;i < length; ++i474 ) { |
50 | 474 | // data[i] = std::byte(value[i]); |
51 | 474 | arena.set(entry, i, (unsigned char)value[i]); |
52 | 474 | } |
53 | 51 | } |
54 | | /// \brief Initialises a DataRef from a std::byte array of length `length` |
55 | 2 | DataRef(const std::byte* value, std::size_t length) : entry(arena.allocate(length)) { |
56 | 10 | for (std::size_t i = 0;i < length; ++i8 ) { |
57 | 8 | // data[i] = value[i]; |
58 | 8 | arena.set(entry, i, (unsigned char)value[i]); |
59 | 8 | } |
60 | 2 | } |
61 | | /// \brief Initialises a DataRef from a string of chars |
62 | | DataRef(const std::string& from) : entry(arena.allocate(from.size())) { |
63 | | for (std::size_t i = 0;i < from.size(); ++i) { |
64 | | // data[i] = value[i]; |
65 | | arena.set(entry, i, (unsigned char)from[i]); |
66 | | } |
67 | | } |
68 | | /// \brief Initialises a DataRef copying another data object (uses more data, to ensure only one object owns the entry) |
69 | 410 | DataRef(const DataRef& from) : entry(arena.allocate(from.entry.byteLength)) { |
70 | 3.11k | for (std::size_t i = 0;i < from.size(); ++i2.70k ) { |
71 | 2.70k | arena.set(entry, i, from.arena.get(from.entry,i)); |
72 | 2.70k | } |
73 | 410 | } |
74 | | |
75 | | /// \brief Initialises a DataRef with count number of repeating bytes |
76 | 102 | DataRef(std::byte repeating, std::size_t count) : entry(arena.allocate(count)) { |
77 | 706 | for (std::size_t i = 0;i < count; ++i604 ) { |
78 | 604 | arena.set(entry,i,(unsigned char)repeating); |
79 | 604 | } |
80 | 102 | } |
81 | | /// \brief Initialises a DataRef with reserveLength bytes of undefined data |
82 | 201 | DataRef(std::size_t reserveLength) : entry(arena.allocate(reserveLength)) { |
83 | 201 | ; |
84 | 201 | } |
85 | | |
86 | | |
87 | | // Data(Base64String from); // use Base64String.from(std::string).decode() instea |
88 | | /// \brief Copy assign operator. Copies the data to be sure only one object owns the entry |
89 | | DataRef& operator=(const DataRef& other) |
90 | 35 | { |
91 | 35 | entry = arena.allocate(other.entry.byteLength); |
92 | 487 | for (std::size_t i = 0;i < other.size(); ++i452 ) { |
93 | 452 | arena.set(entry, i, other.arena.get(other.entry,i)); |
94 | 452 | } |
95 | 35 | return *this; |
96 | 35 | } |
97 | | |
98 | | /// \brief Default destructor |
99 | 1.09k | ~DataRef() { |
100 | 1.09k | clear(); |
101 | 1.09k | } |
102 | | |
103 | | // std::string base64EncodedString(); // use Base64String.encode(Data) instead |
104 | | /// \brief Creates a new DataRef object from a hexadecimal encoded string |
105 | | static DataRef fromHexEncodedString(const std::string& hex) |
106 | 138 | { |
107 | 138 | // parse string |
108 | 138 | const std::size_t length = hex.size(); |
109 | 138 | std::string hexInput; |
110 | 138 | // Input size check - two characters per single byte |
111 | 138 | if (1 == length % 2) { |
112 | 0 | // invalid format - not an even number of characters |
113 | 0 | // Prepend input with a 0. (Note '8' and '08' in hex are the same) |
114 | 0 | hexInput += "0"; |
115 | 0 | } |
116 | 138 | hexInput += hex; |
117 | 138 | |
118 | 138 | DataRef d(hexInput.size() / 2); |
119 | 138 | |
120 | 2.32k | for (std::size_t i = 0; i < hexInput.size(); i += 22.18k ) { |
121 | 2.18k | std::string byteString = hexInput.substr(i, 2); |
122 | 2.18k | std::byte byte = std::byte(strtol(byteString.c_str(), NULL, 16)); |
123 | 2.18k | // d.data.push_back(byte); |
124 | 2.18k | arena.set(d.entry,i / 2, (unsigned char)byte); |
125 | 2.18k | } |
126 | 138 | |
127 | 138 | return d; |
128 | 138 | } |
129 | | |
130 | | /// \brief Returns the hex encoded string represetation as the description |
131 | | std::string description() const |
132 | 45 | { |
133 | 45 | return hexEncodedString(); |
134 | 45 | } |
135 | | |
136 | | /// \brief Returns a NEWLY allocated DataRef instance returning a subset of this instance |
137 | | DataRef subdata(std::size_t offset) const |
138 | 4 | { |
139 | 4 | if (offset >= entry.byteLength) { |
140 | 2 | return DataRef(0); |
141 | 2 | } |
142 | 2 | DataRef copy(entry.byteLength - offset); |
143 | 9 | for (std::size_t i = 0;i < entry.byteLength - offset;++i7 ) { |
144 | 7 | copy.arena.set(copy.entry,i,arena.get(entry,i + offset)); |
145 | 7 | } |
146 | 2 | // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data)); |
147 | 2 | return copy; |
148 | 2 | } |
149 | | |
150 | | /// \brief Returns a NEWLY allocated DataRef instance returning a subset of this instance |
151 | | DataRef subdata(std::size_t offset, std::size_t length) const |
152 | 26 | { |
153 | 26 | // Note: offset if passed as -1 could be MAX_LONG_LONG, so check it on its own too |
154 | 26 | if (offset >= entry.byteLength) { |
155 | 3 | return DataRef(0); |
156 | 3 | } |
157 | 23 | std::size_t correctedLength = length; |
158 | 23 | if (length > entry.byteLength || length + offset > entry.byteLength22 ) { |
159 | 4 | correctedLength = entry.byteLength - offset; |
160 | 4 | } |
161 | 23 | DataRef copy(correctedLength); |
162 | 23 | // Note the below is necessary as calling with (4,-1), the second condition IS valid! |
163 | 23 | // if (length > entry.byteLength || offset + length > entry.byteLength) { |
164 | 23 | // for (std::size_t i = 0;i < entry.byteLength - offset;++i) { |
165 | 23 | // copy.arena.set(copy.entry,i,arena.get(entry,offset + i)); |
166 | 23 | // } |
167 | 23 | // // std::copy(data.begin() + offset, data.end(), std::back_inserter(copy.data)); |
168 | 23 | // } else { |
169 | 106 | for (std::size_t i = 0;i < correctedLength;++i83 ) { |
170 | 83 | copy.arena.set(copy.entry,i,arena.get(entry,offset + i)); |
171 | 83 | } |
172 | 23 | // std::copy(data.begin() + offset, data.begin() + offset + length, std::back_inserter(copy.data)); |
173 | 23 | // } |
174 | 23 | return copy; |
175 | 23 | } |
176 | | |
177 | | /// \brief Returns the individual byte at index position, or a byte value of zero if index is out of bounds. |
178 | 2.57k | std::byte at(std::size_t index) const { |
179 | 2.57k | if (index > (unsigned short)(entry.byteLength - 1)) { |
180 | 2 | return std::byte(0); |
181 | 2 | } |
182 | 2.57k | return std::byte(arena.get(entry,index)); |
183 | 2.57k | } |
184 | | |
185 | | /// \brief |
186 | | /// Assigns the data in-place, reserving the size required if |
187 | | /// the current size is too small. |
188 | | /// |
189 | | /// Avoids repeated reallocation of memory on a copy. |
190 | | void assign(const DataRef& other) |
191 | 0 | { |
192 | 0 | if (other.size() > entry.byteLength) { |
193 | 0 | arena.reserve(entry,other.size()); |
194 | 0 | } |
195 | 0 | for (std::size_t pos = 0; pos < other.size();++pos) { |
196 | 0 | arena.set(entry,pos,other.arena.get(other.entry,pos)); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /// \brief Copies another DataRef into this instance, expanding if required |
201 | | void append(const DataRef& rawData, std::size_t offset, std::size_t length) |
202 | | { |
203 | | auto curSize = entry.byteLength; |
204 | | arena.reserve(entry,curSize + length); |
205 | | for (std::size_t pos = 0; pos < length;++pos) { |
206 | | arena.set(entry,curSize + pos,rawData.arena.get(rawData.entry,pos + offset)); |
207 | | } |
208 | | // std::copy(rawData.data.begin() + offset, |
209 | | // rawData.data.begin() + offset + length, |
210 | | // std::back_inserter(data) |
211 | | // ); |
212 | | } |
213 | | |
214 | | /// \brief Appends a set of characters to the end of this DataRef |
215 | | void append(const std::string& rawData) |
216 | 4 | { |
217 | 4 | auto curSize = entry.byteLength; |
218 | 4 | arena.reserve(entry,curSize + rawData.size()); |
219 | 74 | for (std::size_t pos = 0; pos < rawData.size();++pos70 ) { |
220 | 70 | arena.set(entry,curSize + pos,rawData[pos]); |
221 | 70 | } |
222 | 4 | } |
223 | | |
224 | | /// \brief Copies a uint8_t array onto the end of this instance, expanding if necessary |
225 | | void append(const std::uint8_t* rawData, std::size_t offset, std::size_t length) |
226 | 24 | { |
227 | 24 | auto curSize = entry.byteLength; |
228 | 24 | arena.reserve(entry,curSize + length); |
229 | 170 | for (std::size_t i = 0;i < length;++i146 ) { |
230 | 146 | arena.set(entry,curSize + i,(unsigned char)(rawData[offset + i])); |
231 | 146 | // arena.set(entry,curSize,std::byte(rawData[offset + i])); |
232 | 146 | } |
233 | 24 | } |
234 | | |
235 | | /// \brief Appends the specified DataRef to this one, but in its reverse order |
236 | | void appendReversed(const DataRef& rawData, std::size_t offset, std::size_t length) |
237 | 6 | { |
238 | 6 | if (offset > rawData.size()) { |
239 | 0 | return; // append nothing - out of range |
240 | 0 | } |
241 | 6 | std::size_t checkedLength = length; |
242 | 6 | if (length > (rawData.size() - offset)) { |
243 | 4 | checkedLength = rawData.size() - offset; |
244 | 4 | } |
245 | 6 | auto curSize = entry.byteLength; |
246 | 6 | arena.reserve(entry,curSize + checkedLength); |
247 | 24 | for (std::size_t i = 0;i < checkedLength;++i18 ) { |
248 | 18 | arena.set(entry,curSize + i, |
249 | 18 | rawData.arena.get(rawData.entry,offset + (checkedLength - i - 1))); |
250 | 18 | // std::reverse_copy(rawData.data.begin() + offset, |
251 | 18 | // rawData.data.begin() + offset + checkedLength, |
252 | 18 | // std::back_inserter(data) |
253 | 18 | // ); |
254 | 18 | } |
255 | 6 | } |
256 | | |
257 | | /// \brief Appends the specified DataRef to this one |
258 | | void append(const DataRef& rawData) |
259 | 26 | { |
260 | 26 | auto orig = entry.byteLength; |
261 | 26 | arena.reserve(entry,rawData.size() + orig); |
262 | 204 | for (std::size_t pos = 0; pos < rawData.size();++pos178 ) { |
263 | 178 | arena.set(entry,orig + pos,rawData.arena.get(rawData.entry,pos)); |
264 | 178 | } |
265 | 26 | // std::copy(rawData.data.begin(), rawData.data.end(), std::back_inserter(data)); |
266 | 26 | } |
267 | | |
268 | | /// \brief Appends a single byte |
269 | | void append(std::byte rawData) |
270 | 176 | { |
271 | 176 | std::size_t curSize = entry.byteLength; |
272 | 176 | arena.reserve(entry,curSize + 1); |
273 | 176 | // data.push_back(rawData); |
274 | 176 | arena.set(entry,curSize,(unsigned char)rawData); |
275 | 176 | // curSize++; |
276 | 176 | } |
277 | | |
278 | | /// \brief appends a single uint8_t |
279 | | void append(uint8_t rawData) |
280 | 11 | { |
281 | 11 | std::size_t curSize = entry.byteLength; |
282 | 11 | arena.reserve(entry,curSize + 1); // C++ ensures types are AT LEAST x bits |
283 | 11 | // arena.set(entry,curSize,std::byte(rawData)); |
284 | 11 | arena.set(entry,curSize,(unsigned char)(rawData)); |
285 | 11 | // curSize++; |
286 | 11 | } |
287 | | |
288 | | /// \brief Appends a single uint16_t |
289 | | void append(uint16_t rawData) |
290 | 9 | { |
291 | 9 | std::size_t curSize = entry.byteLength; |
292 | 9 | arena.reserve(entry,curSize + 2); // C++ ensures types are AT LEAST x bits |
293 | 9 | arena.set(entry,curSize,(unsigned char)(rawData & 0xff)); |
294 | 9 | arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8)); |
295 | 9 | } |
296 | | |
297 | | /// \brief Appends a single uint32_t |
298 | | void append(uint32_t rawData) |
299 | 3 | { |
300 | 3 | std::size_t curSize = entry.byteLength; |
301 | 3 | arena.reserve(entry,curSize + 4); // C++ ensures types are AT LEAST x bits |
302 | 3 | arena.set(entry,curSize,(unsigned char)(rawData & 0xff)); |
303 | 3 | arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8)); |
304 | 3 | arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16)); |
305 | 3 | arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24)); |
306 | 3 | } |
307 | | |
308 | | /// \brief Appends a single uint64_t |
309 | | void append(uint64_t rawData) |
310 | 3 | { |
311 | 3 | std::size_t curSize = entry.byteLength; |
312 | 3 | arena.reserve(entry,curSize + 8); // C++ ensures types are AT LEAST x bits |
313 | 3 | arena.set(entry,curSize,(unsigned char)(rawData & 0xff)); |
314 | 3 | arena.set(entry,curSize + 1,(unsigned char)(rawData >> 8)); |
315 | 3 | arena.set(entry,curSize + 2,(unsigned char)(rawData >> 16)); |
316 | 3 | arena.set(entry,curSize + 3,(unsigned char)(rawData >> 24)); |
317 | 3 | arena.set(entry,curSize + 4,(unsigned char)(rawData >> 32)); |
318 | 3 | arena.set(entry,curSize + 5,(unsigned char)(rawData >> 40)); |
319 | 3 | arena.set(entry,curSize + 6,(unsigned char)(rawData >> 48)); |
320 | 3 | arena.set(entry,curSize + 7,(unsigned char)(rawData >> 56)); |
321 | 3 | } |
322 | | |
323 | | /// \brief Returns whether reading a single uint8_t to `into` at `fromIndex` was successful |
324 | | bool uint8(std::size_t fromIndex, uint8_t& into) const noexcept |
325 | 1.56k | { |
326 | 1.56k | if (fromIndex > (unsigned short)(entry.byteLength - 1)) { |
327 | 0 | return false; |
328 | 0 | } |
329 | 1.56k | into = std::uint8_t(arena.get(entry,fromIndex)); |
330 | 1.56k | return true; |
331 | 1.56k | } |
332 | | |
333 | | /// \brief Returns whether reading a single uint16_t to `into` at `fromIndex` was successful |
334 | | bool uint16(std::size_t fromIndex, uint16_t& into) const noexcept |
335 | 7 | { |
336 | 7 | if (fromIndex > (unsigned short)(entry.byteLength - 2)) { |
337 | 0 | return false; |
338 | 0 | } |
339 | 7 | into = (std::uint16_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | std::uint16_t(std::uint8_t(arena.get(entry,fromIndex))); |
340 | 7 | return true; |
341 | 7 | } |
342 | | |
343 | | /// \brief Returns whether reading a single uint32_t to `into` at `fromIndex` was successful |
344 | | bool uint32(std::size_t fromIndex, uint32_t& into) const noexcept |
345 | 2 | { |
346 | 2 | if (fromIndex > entry.byteLength - 4) { |
347 | 0 | return false; |
348 | 0 | } |
349 | 2 | into = std::uint32_t(std::uint8_t(arena.get(entry,fromIndex))) | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | |
350 | 2 | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) | (std::uint32_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24); |
351 | 2 | return true; |
352 | 2 | } |
353 | | |
354 | | /// \brief Returns whether reading a single uint64_t to `into` at `fromIndex` was successful |
355 | | bool uint64(std::size_t fromIndex, uint64_t& into) const noexcept |
356 | 3 | { |
357 | 3 | if (entry.byteLength < 8 || fromIndex > entry.byteLength - 8) { |
358 | 0 | return false; |
359 | 0 | } |
360 | 3 | into = (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 7))) << 56) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 6))) << 48) | |
361 | 3 | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 5))) << 40) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 4))) << 32) | |
362 | 3 | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 3))) << 24) | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 2))) << 16) | |
363 | 3 | (std::uint64_t(std::uint8_t(arena.get(entry,fromIndex + 1))) << 8) | std::uint64_t(std::uint8_t(arena.get(entry,fromIndex))); |
364 | 3 | return true; |
365 | 3 | } |
366 | | |
367 | | // TODO signed versions of the above functions too |
368 | | /// \brief Equality operator for another DataRef instance (same memory arena) |
369 | | bool operator==(const DataRef& other) const noexcept |
370 | 23 | { |
371 | 23 | if (size() != other.size()) { |
372 | 0 | return false; |
373 | 0 | } |
374 | 23 | //if (hashCode() != other.hashCode()) { |
375 | 23 | // return false; |
376 | 23 | //} |
377 | 23 | // else compare each value |
378 | 23 | |
379 | 23 | // alternatively, cheat... |
380 | 23 | return hashCode() == other.hashCode(); // Somewhat naughty |
381 | 23 | } |
382 | | |
383 | | /// \brief Inequality operator for another DataRef instance (same memory arena) |
384 | | bool operator!=(const DataRef& other) const noexcept |
385 | 8 | { |
386 | 8 | if (size() != other.size()) { |
387 | 0 | return true; |
388 | 0 | } |
389 | 8 | return hashCode() != other.hashCode(); |
390 | 8 | } |
391 | | |
392 | | /// \brief Less than operator for another DataRef instance (same memory arena) |
393 | | bool operator<(const DataRef& other) const noexcept |
394 | 4 | { |
395 | 4 | return hashCode() < other.hashCode(); |
396 | 4 | } |
397 | | |
398 | | /// \brief Greater than operator for another DataRef instance (same memory arena) |
399 | | bool operator>(const DataRef& other) const noexcept |
400 | 0 | { |
401 | 0 | return hashCode() > other.hashCode(); |
402 | 0 | } |
403 | | |
404 | | /// \brief Returns a new DataRef instance with the same data as this one, but in the reverse order |
405 | | DataRef reversed() const |
406 | 32 | { |
407 | 32 | DataRef result(entry.byteLength); |
408 | 32 | // result.reserve(entry.byteLength); |
409 | 216 | for (std::size_t pos = 0;pos < entry.byteLength;++pos184 ) { |
410 | 184 | result.arena.set(result.entry,pos,arena.get(entry,entry.byteLength - pos - 1)); |
411 | 184 | } |
412 | 32 | // std::reverse_copy(data.begin(),data.end(), |
413 | 32 | // std::back_inserter(result.data) |
414 | 32 | // ); |
415 | 32 | return result; |
416 | 32 | } |
417 | | |
418 | | /// \brief Returns the same order of bytes, but with the bits in each byte reversed |
419 | | DataRef reverseEndianness() const |
420 | 1 | { |
421 | 1 | DataRef result(entry.byteLength); |
422 | 1 | // result.data.reserve(entry.byteLength); |
423 | 1 | |
424 | 1 | // Keep byte order intact (caller could use reversed() to change that) |
425 | 1 | // but reverse the order of the individual bits by each byte |
426 | 1 | std::uint8_t value, original; |
427 | 6 | for (std::size_t i = 0;i < entry.byteLength;++i5 ) { |
428 | 5 | original = std::uint8_t(arena.get(entry,i)); |
429 | 5 | value = 0; |
430 | 45 | for (int b = 0;b < 8;++b40 ) { |
431 | 40 | if ((original & (1 << b)) > 0) { |
432 | 20 | value |= 1 << (7 - b); |
433 | 20 | } |
434 | 40 | } |
435 | 5 | // result.data[i] = std::byte(value); |
436 | 5 | result.arena.set(result.entry,entry.byteLength - i - 1,value); |
437 | 5 | } |
438 | 1 | |
439 | 1 | return result; |
440 | 1 | } |
441 | | |
442 | | /// \brief Returns a hex encoded string of this binary data |
443 | | std::string hexEncodedString() const noexcept |
444 | 95 | { |
445 | 95 | if (0 == entry.byteLength) { |
446 | 4 | return ""; |
447 | 4 | } |
448 | 91 | std::string result; |
449 | 91 | std::size_t size = entry.byteLength; |
450 | 91 | result.reserve(size * 2); |
451 | 91 | std::size_t v; |
452 | 742 | for (std::size_t i = 0; i < size; ++i651 ) { |
453 | 651 | // v = std::size_t(data.at(i)); |
454 | 651 | v = std::size_t(arena.get(entry,i)); |
455 | 651 | result += hexChars[0x0F & (v >> 4)]; // MSB |
456 | 651 | result += hexChars[0x0F & v ]; // LSB |
457 | 651 | } |
458 | 91 | return result; |
459 | 91 | } |
460 | | |
461 | | /// \brief Returns the hash code of this instance |
462 | | std::size_t hashCode() const noexcept |
463 | 86 | { |
464 | 86 | // TODO consider a faster (E.g. SIMD) algorithm or one with less hotspots (see hashdos attacks) |
465 | 86 | return std::hash<DataRef<MemoryArenaT>>{}(*this); |
466 | 86 | } |
467 | | |
468 | | /// \brief Returns the size in allocated bytes of this instance |
469 | 6.03k | std::size_t size() const noexcept{ |
470 | 6.03k | return entry.byteLength; |
471 | 6.03k | } |
472 | | // TODO support other C++ STD container type functions to allow iteration over data elements (uint8) |
473 | | |
474 | | /// \brief Clears (deallocates) the bytes referred to by this instance |
475 | | void clear() noexcept |
476 | 1.11k | { |
477 | 1.11k | arena.deallocate(entry); |
478 | 1.11k | } |
479 | | |
480 | | static MemoryArenaT& getArena() { |
481 | | return arena; |
482 | | } |
483 | | |
484 | | protected: |
485 | | static const char hexChars[]; |
486 | | static MemoryArenaT arena; |
487 | | MemoryArenaEntry entry; |
488 | | }; |
489 | | |
490 | | |
491 | | |
492 | | |
493 | | /// \brief Instantiates the MemoryArena instance used by all DataRefs that share it |
494 | | template <typename MemoryArenaT> |
495 | | MemoryArenaT DataRef<MemoryArenaT>::arena = MemoryArenaT(); |
496 | | |
497 | | template <typename MemoryArenaT> |
498 | | const char DataRef<MemoryArenaT>::hexChars[] = { |
499 | | '0','1','2','3','4','5','6','7', |
500 | | '8','9','a','b','c','d','e','f' |
501 | | }; |
502 | | |
503 | | /// \brief Defaults references to Data to equal the DataRef with the default Memory Arena dimensions, unless HERALD_MEMORYARENA_MAX is specified |
504 | | /// May also have its allocation size set by HERALD_MEMORYARENA_PAGE. Note this only takes |
505 | | /// affect if HERALD_MEMORYARENA_MAX is also specified. |
506 | | using Data = DataRef<>; // uses MemoryArenaT<4096,8> |
507 | | |
508 | | |
509 | | |
510 | | |
511 | | /// \brief Represents a fixed array of Data references using the default memory arena that tracks its own in-use size |
512 | | template <std::size_t maxSize = 8> |
513 | | class DataSections { |
514 | | public: |
515 | | static constexpr std::size_t MaxSize = maxSize; |
516 | | |
517 | | /// \brief Default constructors. No memory allocation other than size (8 bytes) |
518 | | DataSections() noexcept : sections(), sz(0) {} |
519 | | /// \brief Default destructor |
520 | | ~DataSections() noexcept = default; |
521 | | |
522 | | /// \brief Adds a new section, if room exists. Otherwise quietly performs a NO OP. |
523 | | void append(const Data& toCopy) noexcept { |
524 | | if (sz >= MaxSize) { |
525 | | return; |
526 | | } |
527 | | sections[sz] = toCopy; // copy assign operator |
528 | | ++sz; |
529 | | } |
530 | | |
531 | | /// \brief Returns the number of dataref elements in use |
532 | | std::size_t size() noexcept { |
533 | | return sz; |
534 | | } |
535 | | |
536 | | /// \brief Returns the DataRef at the relevant index, or an empty DataRef |
537 | | const Data& get(std::size_t index) noexcept { |
538 | | if (index >= MaxSize) { |
539 | | return emptyRef; |
540 | | } |
541 | | return sections[index]; |
542 | | } |
543 | | |
544 | | /// \brief Returns the DataRef at the relevant index, or an empty DataRef |
545 | | const Data& operator[](std::size_t index) noexcept { |
546 | | return get(index); |
547 | | } |
548 | | |
549 | | private: |
550 | | static Data emptyRef; |
551 | | std::array<Data,MaxSize> sections; |
552 | | std::size_t sz; |
553 | | }; |
554 | | |
555 | | /// \brief Reference to empty data item for optional/empty return by ref |
556 | | template <std::size_t maxSize> |
557 | | Data DataSections<maxSize>::emptyRef = Data(); |
558 | | |
559 | | } // end namespace |
560 | | } // end namespace |
561 | | |
562 | | namespace std { |
563 | | template <typename MemoryArenaT> |
564 | | inline std::ostream& operator<<(std::ostream &os, const herald::datatype::DataRef<MemoryArenaT>& d) |
565 | 1 | { |
566 | 1 | return os << d.hexEncodedString(); |
567 | 1 | } |
568 | | |
569 | | inline void hash_combine_impl(std::size_t& seed, std::size_t value) |
570 | 1.46k | { |
571 | 1.46k | seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2); |
572 | 1.46k | } |
573 | | |
574 | | template<typename MemoryArenaT> |
575 | | struct hash<herald::datatype::DataRef<MemoryArenaT>> |
576 | | { |
577 | | size_t operator()(const herald::datatype::DataRef<MemoryArenaT>& v) const |
578 | 244 | { |
579 | 244 | std::size_t hv = 0; |
580 | 244 | std::uint8_t ui = 0; |
581 | 244 | bool ok; |
582 | 1.71k | for (std::size_t pos = 0;pos < v.size();++pos1.46k ) { |
583 | 1.46k | ok = v.uint8(pos,ui); |
584 | 1.46k | hash_combine_impl(hv, std::hash<std::uint8_t>()(ui)); |
585 | 1.46k | } |
586 | 244 | return hv; |
587 | 244 | } |
588 | | }; |
589 | | } // end namespace |
590 | | |
591 | | #endif |